/*
    Copyright 2010 Northbranchlogic, Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

package com.norbl.runjrun;

import com.norbl.util.*;

/** Represents the state of a {@link RunnerNode} instance and the
 *  host it is running on. The host can be any (net accessible) computer,
 *  although it is ususally an ec2 instance.
 *
 * @author Barnet Wagman
 */
public class NodeState extends DbEl {

        /** 'nil' status indicates that a node that was formerly
         *  running is no longer sending pulses or otherwise
         *  updating its status.  A 'nil' node is a candidate
         *  for removal from the node list or restarting (by rebooting
         *  its instance).
         */
    public enum Status {
        idle, active, nil, no_pulse;
    };

        /** States are defined per the AWS EC2 api for
         *  "RunningInstanceItemType/InstanceType".  In addition
         *  there is a 'nil' state, which indicates that instance
         *  associated with a node cannot be found.
         */
    public static enum InstanceState {
        pending, running, shutting_down, terminated,
        nil, not_ec2;

        public static InstanceState stateOf(String stateName) {
            if ( stateName == null ) return(InstanceState.nil);
            else if ( "shutting-down".equals(stateName) )
                return(shutting_down);
            else return( valueOf(stateName) );
        }

        boolean isRunning() { 
            return( this.equals(InstanceState.running) );
        }
    }

    public String host;
    public String instanceID;
    public InstanceState instanceState;
    public Long launchTime;
    public Status status;

        /** Last time at which the status was set. */
    public Long statusTime;
    public String runningTask;
    

    public NodeState(String host,
                     String instanceID,
                     InstanceState instanceState,
                     Long launchTime,
                     Status status,
                     Long statusTime,
                     String runningTask) {
        this.host = host;
        this.instanceID = instanceID;
        this.instanceState = instanceState;
        this.launchTime = launchTime;
        this.status = status;
        this.statusTime = statusTime;
        this.runningTask = runningTask;
    }

    public NodeState(PropertyList pl,boolean allowMissingFields) {
        super(pl,allowMissingFields);
    }

    public NodeState() {}

    public DbElField[] getRelevantFields() {

        try {
            Class c = NodeState.class;

            return( new DbElField[] {
                new DbElField(c.getField("host"),host,"Host"),
                new DbElField(c.getField("instanceID"),instanceID,"Instance ID"),
                new DbElField(c.getField("instanceState"),instanceState,"Instance state"),
                new TimeField(c.getField("launchTime"),launchTime,"Launch time"),
                new DbElField(c.getField("status"),status,"Status"),
                new TimeField(c.getField("statusTime"),statusTime,"Status time"),
                new DbElField(c.getField("runningTask"),runningTask,"Running task")               
            } );
        }
        catch(Exception x) { throw new RuntimeException(x); }
    }

    public DbElField[] getDisplayFields() {
        try {
            Class c = NodeState.class;

            return( new DbElField[] {
                new DbElField(c.getField("host"),host,"Host"),
                new DbElField(c.getField("instanceID"),instanceID,"Instance ID"),
                new InstanceStateField(c.getField("instanceState"),
                                       instanceState,"Instance state"),
                new TimeField(c.getField("launchTime"),launchTime,"Launch time"),
                new DbElField(c.getField("status"),status,"Status"),
                new TimeField(c.getField("statusTime"),statusTime,"Status time"),
                new DbElField(c.getField("runningTask"),runningTask,"Running task")
            } );
        }
        catch(Exception x) { throw new RuntimeException(x); }
    }

    public Object getPropertyValue(Property p) {
        if ( p == null ) return(null);
        if ( p.value == null ) return(null);
        if ( p.name.equals("status") && (p.value instanceof String) )
            return( Status.valueOf( (String) p.value ) );
        if ( p.name.equals("instanceState") && (p.value instanceof String) )
            return( InstanceState.stateOf( (String) p.value ) );
        else return(p.value);
    }

        /** Tests whether a node is running on an ec2 instance or some
         *  other sort of system.
         */
    public boolean isOnEc2Instance() {

        return( (instanceID != null) &&
                !instanceID.equals(Constants.NIL) &&
                !isNonEc2Host(instanceID) );
    }

        /** If a {@link RunnerNode} is on a non-ec2 system (i.e. it is a
         *  {@link RunnerNodeNonEc2} not {@link RunnerNodeEc2}), it's
         *  instance ID field (in the sdb node state table) ends with
         *  {@link Constants#NOT_EC2}.
         * 
         * @param instanceID
         * @return true if the host is <i>not</i> and EC2 instances, else false.
         */
    public static boolean isNonEc2Host(String instanceID) {
        return( instanceID.endsWith(Constants.NOT_EC2) );
    }
    
    public boolean isAlive() {
        
        if (status == null) return(false);
        
        return( status.equals(NodeState.Status.active) ||
                status.equals(NodeState.Status.idle) );
    }
    
    public boolean instanceIsRunning() {
        if ( instanceState == null ) return(false);
        else return( instanceState.equals(InstanceState.running) );
    }

        /** Is true if the instance has state 'running' and the
         *  node is alive.  If the host is not an ec2 instance,
         *  only the node status is relevant.
         *
         * @return true if instance state is 'running', else false.
         *
         */
    public boolean isRunning() {

        if ( isOnEc2Instance() ) {
            return(isAlive() && instanceIsRunning());
        }
        else return(isAlive());
    }
}
